home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Manuels & Misc / Assembly / AOA.ZIP / CH18 / KEYEVAL.ASM < prev    next >
Encoding:
Assembly Source File  |  1994-07-15  |  18.2 KB  |  839 lines

  1. ; This is an example of an active TSR that counts keyboard interrupts
  2. ; once activated.  Every minute it writes the number of keyboard
  3. ; interrupts that occurred in the previous minute to an output file.
  4. ; This continues until the user removes the program from memory.
  5. ;
  6. ;
  7. ; Usage:
  8. ;    KEYEVAL  filename    - Begins logging keystroke data  to
  9. ;                  this file.
  10. ;
  11. ;    KEYEVAL  REMOVE        - Removes the resident program from
  12. ;                  memory.
  13. ;
  14. ;
  15. ; This TSR checks to make sure there isn't a copy already active in
  16. ; memory.  When doing disk I/O from the interrupts, it checks to make
  17. ; sure DOS isn't busy and it preserves application globals (PSP, DTA,
  18. ; and extended error info).  When removing itself from memory, it
  19. ; makes sure there are no other interrupts chained into any of its
  20. ; interrupts before doing the remove.
  21. ;
  22. ; The resident segment definitions must come before everything else.
  23.  
  24. ResidentSeg    segment    para public 'Resident'
  25. ResidentSeg    ends
  26.  
  27. EndResident    segment    para public 'EndRes'
  28. EndResident    ends
  29.  
  30.         .xlist
  31.         .286
  32.         include     stdlib.a
  33.         includelib    stdlib.lib
  34.         .list
  35.  
  36.  
  37. ; Resident segment that holds the TSR code:
  38.  
  39. ResidentSeg    segment    para public 'Resident'
  40.         assume    cs:ResidentSeg, ds:nothing
  41.  
  42. ; Int 2Fh ID number for this TSR:
  43.  
  44. MyTSRID        byte    0
  45.  
  46. ; The following variable counts the number of keyboard interrupts
  47.  
  48. KeyIntCnt    word    0
  49.  
  50. ; Counter counts off the number of milliseconds that pass, SecCounter
  51. ; counts off the number of seconds (up to 60).
  52.  
  53. Counter        word    0
  54. SecCounter    word    0
  55.  
  56. ; FileHandle is the handle for the log file:
  57.  
  58. FileHandle    word    0
  59.  
  60. ; NeedIO determines if we have a pending I/O opearation.
  61.  
  62. NeedIO        word    0
  63.  
  64. ; PSP is the psp address for this program.
  65.  
  66. PSP        word    0
  67.  
  68. ; Variables to tell us if DOS, INT 13h, or INT 16h are busy:
  69.  
  70. InInt13        byte    0
  71. InInt16        byte    0
  72. InDOSFlag    dword    ?
  73.  
  74. ; These variables contain the original values in the interrupt vectors
  75. ; we've patched.
  76.  
  77. OldInt9        dword    ?
  78. OldInt13    dword    ?
  79. OldInt16    dword    ?
  80. OldInt1C    dword    ?
  81. OldInt28    dword    ?
  82. OldInt2F    dword    ?
  83.  
  84.  
  85. ; DOS data structures:
  86.  
  87. ExtErr        struct
  88. eeAX        word    ?
  89. eeBX        word    ?
  90. eeCX        word    ?
  91. eeDX        word    ?
  92. eeSI        word    ?
  93. eeDI        word    ?
  94. eeDS        word    ?
  95. eeES        word    ?
  96.         word    3 dup (0)
  97. ExtErr        ends
  98.  
  99.  
  100.  
  101. XErr        ExtErr    {}        ;Extended Error Status.
  102. AppPSP        word    ?        ;Application PSP value.
  103. AppDTA        dword    ?        ;Application DTA address.
  104.  
  105.  
  106. ; The following data is the output record.  After storing this data
  107. ; to these variables, the TSR writes this data to disk.
  108.  
  109. month        byte    0
  110. day        byte    0
  111. year        word    0
  112. hour        byte    0
  113. minute        byte    0
  114. second        byte    0
  115. Keystrokes    word    0
  116. RecSize        =    $-month
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124. ; MyInt9-    The system calls this routine every time a keyboard
  125. ;        interrupt occus.  This routine increments the
  126. ;        KeyIntCnt variable and then passes control on to the
  127. ;        original Int9 handler.
  128.  
  129. MyInt9        proc    far
  130.         inc    ResidentSeg:KeyIntCnt
  131.         jmp    ResidentSeg:OldInt9
  132. MyInt9        endp
  133.  
  134.  
  135.  
  136.  
  137.  
  138. ; MyInt1C-    Timer interrupt.  This guy counts off 60 seconds and then
  139. ;        attempts to write a record to the output file.  Of course,
  140. ;        this call has to jump through all sorts of hoops to keep
  141. ;        from reentering DOS and other problematic code.
  142.  
  143. MyInt1C        proc    far
  144.         assume    ds:ResidentSeg
  145.  
  146.         push    ds
  147.         push    es
  148.         pusha                ;Save all the registers.
  149.         mov    ax, ResidentSeg
  150.         mov    ds, ax
  151.  
  152.         pushf
  153.         call    OldInt1C
  154.  
  155. ; First things first, let's bump our interrupt counter so we can count
  156. ; off a minute.  Since we're getting interrupted about every 54.92549
  157. ; milliseconds, let's shoot for a little more accuracy than 18 times
  158. ; per second so the timings don't drift too much.
  159.  
  160.         add    Counter, 549        ;54.9 msec per int 1C.
  161.         cmp    Counter, 10000        ;1 second.
  162.         jb    NotSecYet
  163.         sub    Counter, 10000
  164.         inc    SecCounter
  165. NotSecYet:
  166.  
  167.  
  168. ; If NEEDIO is not zero, then there is an I/O operation in progress.
  169. ; Do not disturb the output values if this is the case.
  170.  
  171.         cli                ;This is a critical region.
  172.         cmp    NeedIO, 0
  173.         jne    SkipSetNIO
  174.  
  175. ; Okay, no I/O in progress, see if a minute has passed since the last
  176. ; time we logged the keystrokes to the file.  If so, it's time to start
  177. ; another I/O operation.
  178.  
  179.         cmp    SecCounter, 60        ;One minute passed yet?
  180.         jb    Int1CDone
  181.         mov    NeedIO, 1        ;Flag need for I/O.
  182.         mov    ax, KeyIntCnt        ;Copy this to the output
  183.         shr    ax, 1            ; buffer after computing
  184.         mov     KeyStrokes, ax        ; # of keystrokes.
  185.         mov    KeyIntCnt, 0        ;Reset for next minute.
  186.         mov    SecCounter, 0
  187.  
  188. SkipSetNIO:     cmp    NeedIO, 1        ;Is the I/O already in
  189.         jne    Int1CDone        ; progress?  Or done?
  190.  
  191.         call    ChkDOSStatus        ;See if DOS/BIOS are free.
  192.         jnc    Int1CDone        ;Branch if busy.
  193.  
  194.         call    DoIO            ;Do I/O if DOS is free.
  195.  
  196. Int1CDone:    popa
  197.         pop    es
  198.         pop    ds
  199.         iret
  200. MyInt1C        endp
  201.         assume    ds:nothing
  202.  
  203.  
  204. ; MyInt28-    Idle interrupt.  If DOS is in a busy-wait loop waiting for
  205. ;        I/O to complete, it executes an int 28h instruction each
  206. ;        time through the loop.  We can ignore the InDOS and CritErr
  207. ;        flags at that time, and do the I/O if the other interrupts
  208. ;        are free.
  209.  
  210. MyInt28        proc    far
  211.         assume    ds:ResidentSeg
  212.  
  213.         push    ds
  214.         push    es
  215.         pusha                ;Save all the registers.
  216.         mov    ax, ResidentSeg
  217.         mov    ds, ax
  218.  
  219.         pushf                ;Call the next INT 28h
  220.         call    OldInt28        ; ISR in the chain.
  221.  
  222.         cmp    NeedIO, 1        ;Do we have a pending I/O?
  223.         jne    Int28Done
  224.  
  225.         mov    al, InInt13        ;See if BIOS is busy.
  226.         or    al, InInt16
  227.         jne    Int28Done
  228.  
  229.         call    DoIO            ;Go do I/O if BIOS is free.
  230.  
  231. Int28Done:    popa
  232.         pop    es
  233.         pop    ds
  234.         iret
  235. MyInt28        endp
  236.         assume    ds:nothing
  237.  
  238.  
  239. ; MyInt16-    This is just a wrapper for the INT 16h (keyboard trap)
  240. ;        handler.
  241.  
  242. MyInt16        proc    far
  243.         inc    ResidentSeg:InInt16
  244.         pushf
  245.         call    ResidentSeg:OldInt16    ;Call original handler.
  246.         pushf                ;Must preserve flags
  247.         dec    ResidentSeg:InInt16    ; for caller.
  248.         popf
  249.         retf    2            ;Fake IRET to keep flags.
  250. MyInt16        endp
  251.  
  252.  
  253. ; MyInt13-    This is just a wrapper for the INT 13h (disk I/O trap)
  254. ;        handler.
  255.  
  256. MyInt13        proc    far
  257.         inc    ResidentSeg:InInt13
  258.         pushf
  259.         call    ResidentSeg:OldInt13    ;Call original handler.
  260.         pushf                ;Must preserve flags
  261.         dec    ResidentSeg:InInt13    ; for caller.
  262.         popf
  263.         retf    2            ;Fake iret to keep flags.
  264. MyInt13        endp
  265.  
  266.  
  267. ; ChkDOSStatus-    Returns with the carry clear if DOS or a BIOS routine
  268. ;        is busy and we can't interrupt them.
  269.  
  270. ChkDOSStatus    proc    near
  271.         assume    ds:ResidentSeg
  272.         les    bx, InDOSFlag
  273.         mov    al, es:[bx]        ;Get InDOS flag.
  274.         or    al, es:[bx-1]        ;OR with CritErr flag.
  275.         or    al, InInt16        ;OR with our wrapper
  276.         or    al, InInt13        ; values.
  277.         je    Okay2Call
  278.         clc
  279.         ret
  280.  
  281. Okay2Call:    clc
  282.         ret
  283. ChkDOSStatus    endp
  284.         assume    ds:nothing
  285.  
  286.  
  287. ; PreserveDOS-    Gets a copy's of DOS' current PSP, DTA, and extended
  288. ;        error information and saves this stuff.  Then it sets
  289. ;        the PSP to our local PSP and the DTA to PSP:80h.
  290.  
  291. PreserveDOS    proc    near
  292.         assume    ds:ResidentSeg
  293.  
  294.         mov    ah, 51h            ;Get app's PSP.
  295.         int    21h
  296.         mov    AppPSP, bx        ;Save for later
  297.  
  298.         mov    ah, 2Fh            ;Get app's DTA.
  299.         int    21h
  300.         mov    word ptr AppDTA, bx      ;Save for later.
  301.         mov    word ptr AppDTA+2, es
  302.  
  303.         push    ds
  304.         mov    ah, 59h            ;Get extended err info.
  305.         xor    bx, bx
  306.         int    21h
  307.  
  308.         mov    cs:XErr.eeDS, ds
  309.         pop    ds
  310.         mov    XErr.eeAX, ax
  311.         mov    XErr.eeBX, bx
  312.         mov    XErr.eeCX, cx
  313.         mov    XErr.eeDX, dx
  314.         mov    XErr.eeSI, si
  315.         mov    XErr.eeDI, di
  316.         mov    XErr.eeES, es
  317.  
  318. ; Okay, point DOS's pointers at us:
  319.  
  320.         mov    bx, PSP
  321.         mov    ah, 50h            ;Set PSP.
  322.         int    21h
  323.  
  324.         push    ds            ;Set the DTA to
  325.         mov    ds, PSP            ; address PSP:80h
  326.         mov    dx, 80h
  327.         mov    ah, 1Ah            ;Set DTA call.
  328.         int    21h
  329.         pop    ds
  330.  
  331.         ret
  332. PreserveDOS    endp
  333.         assume    ds:nothing
  334.  
  335.  
  336.  
  337. ; RestoreDOS-    Restores DOS' important global data values back to the
  338. ;        application's values.
  339.  
  340. RestoreDOS    proc    near
  341.         assume    ds:ResidentSeg
  342.  
  343.         mov    bx, AppPSP
  344.         mov    ah, 50h            ;Set PSP
  345.         int    21h
  346.  
  347.         push    ds
  348.         lds    dx, AppDTA
  349.         mov    ah, 1Ah            ;Set DTA
  350.         int    21h
  351.         pop    ds
  352.         push    ds
  353.  
  354.         mov    si, offset XErr        ;Saved extended error stuff.
  355.         mov    ax, 5D0Ah        ;Restore XErr call.
  356.         int    21h
  357.         pop    ds
  358.         ret
  359. RestoreDOS    endp
  360.         assume    ds:nothing
  361.  
  362.  
  363. ; DoIO-        This routine processes each of the I/O operations
  364. ;        required to write data to the file.
  365.  
  366. DoIO        proc    near
  367.         assume    ds:ResidentSeg
  368.  
  369.         mov    NeedIO, 0FFh        ;A busy flag for us.
  370.  
  371. ; The following Get Date DOS call may take a while, so turn the
  372. ; interrupts back on (we're clear of the critical section once we
  373. ; write 0FFh to NeedIO).
  374.  
  375.         sti
  376.         call    PreserveDOS        ;Save DOS data.
  377.  
  378.         mov    ah, 2Ah            ;Get Date DOS call
  379.         int    21h
  380.         mov    month, dh
  381.         mov    day, dl
  382.         mov    year, cx
  383.  
  384.         mov    ah, 2Ch            ;Get Time DOS call
  385.         int    21h
  386.         mov    hour, ch
  387.         mov    minute, cl
  388.         mov    second, dh
  389.  
  390.         mov    ah, 40h            ;DOS Write call
  391.         mov    bx, FileHandle        ;Write data to this file.
  392.         mov    cx, RecSize        ;This many bytes.
  393.         mov    dx, offset month    ;Starting at this address.
  394.         int    21h            ;Ignore return errors (!).
  395.         mov    ah, 68h            ;DOS Commit call
  396.         mov    bx, FileHandle        ;Write data to this file.
  397.         int    21h            ;Ignore return errors (!).
  398.  
  399.         mov    NeedIO, 0        ;Ready to start over.
  400.         call    RestoreDOS
  401.  
  402. PhasesDone:    ret
  403. DoIO        endp
  404.         assume    ds:nothing
  405.  
  406.  
  407.  
  408. ; MyInt2F-    Provides int 2Fh (multiplex interrupt) support for this
  409. ;        TSR.  The multiplex interrupt recognizes the following
  410. ;        subfunctions (passed in AL):
  411. ;
  412. ;        00- Verify presence.      Returns 0FFh in AL and a pointer
  413. ;                    to an ID string in es:di if the
  414. ;                    TSR ID (in AH) matches this
  415. ;                    particular TSR.
  416. ;
  417. ;        01- Remove.        Removes the TSR from memory.
  418. ;                    Returns 0 in AL if successful,
  419. ;                    1 in AL if failure.
  420.  
  421. MyInt2F        proc    far
  422.         assume    ds:nothing
  423.  
  424.         cmp    ah, MyTSRID    ;Match our TSR identifier?
  425.         je    YepItsOurs
  426.         jmp    OldInt2F
  427.  
  428. ; Okay, we know this is our ID, now check for a verify vs. remove call.
  429.  
  430. YepItsOurs:    cmp    al, 0        ;Verify Call
  431.         jne    TryRmv
  432.         mov    al, 0ffh    ;Return success.
  433.         lesi    IDString
  434.         iret            ;Return back to caller.
  435.  
  436. IDString    byte    "Keypress Logger TSR",0
  437.  
  438. TryRmv:        cmp    al, 1        ;Remove call.
  439.         jne    IllegalOp
  440.  
  441.         call    TstRmvable    ;See if we can remove this guy.
  442.         je    CanRemove    ;Branch if we can.
  443.         mov    ax, 1        ;Return failure for now.
  444.         iret
  445.  
  446. ; Okay, they want to remove this guy *and* we can remove it from memory.
  447. ; Take care of all that here.
  448.  
  449.         assume    ds:ResidentSeg
  450.  
  451. CanRemove:    push    ds
  452.         push    es
  453.         pusha
  454.         cli            ;Turn off the interrupts while
  455.         mov    ax, 0        ; we mess with the interrupt
  456.         mov    es, ax        ; vectors.
  457.         mov    ax, cs
  458.         mov    ds, ax
  459.  
  460.         mov    ax, word ptr OldInt9
  461.         mov    es:[9*4], ax
  462.         mov    ax, word ptr OldInt9+2
  463.         mov    es:[9*4 + 2], ax
  464.  
  465.         mov    ax, word ptr OldInt13
  466.         mov    es:[13h*4], ax
  467.         mov    ax, word ptr OldInt13+2
  468.         mov    es:[13h*4 + 2], ax
  469.  
  470.         mov    ax, word ptr OldInt16
  471.         mov    es:[16h*4], ax
  472.         mov    ax, word ptr OldInt16+2
  473.         mov    es:[16h*4 + 2], ax
  474.  
  475.         mov    ax, word ptr OldInt1C
  476.         mov    es:[1Ch*4], ax
  477.         mov    ax, word ptr OldInt1C+2
  478.         mov    es:[1Ch*4 + 2], ax
  479.  
  480.         mov    ax, word ptr OldInt28
  481.         mov    es:[28h*4], ax
  482.         mov    ax, word ptr OldInt28+2
  483.         mov    es:[28h*4 + 2], ax
  484.  
  485.         mov    ax, word ptr OldInt2F
  486.         mov    es:[2Fh*4], ax
  487.         mov    ax, word ptr OldInt2F+2
  488.         mov    es:[2Fh*4 + 2], ax
  489.  
  490.  
  491. ; Okay, with that out of the way, let's close the file.
  492. ; Note: INT 2F shouldn't have to deal with DOS busy because it's
  493. ; a passive TSR call.
  494.  
  495.         mov    ah, 3Eh            ;Close file command
  496.         mov    bx, FileHandle
  497.         int    21h
  498.  
  499. ; Okay, one last thing before we quit- Let's give the memory allocated
  500. ; to this TSR back to DOS.
  501.  
  502.         mov    ds, PSP
  503.         mov    es, ds:[2Ch]        ;Ptr to environment block.
  504.         mov    ah, 49h            ;DOS release memory call.
  505.         int    21h
  506.  
  507.         mov    ax, ds            ;Release program code space.
  508.         mov    es, ax
  509.         mov    ah, 49h
  510.         int    21h
  511.  
  512.         popa
  513.         pop    es
  514.         pop    ds
  515.         mov    ax, 0            ;Return Success.
  516.         iret
  517.  
  518.  
  519. ; They called us with an illegal subfunction value.  Try to do as little
  520. ; damage as possible.
  521.  
  522. IllegalOp:    mov    ax, 0        ;Who knows what they were thinking?
  523.         iret
  524. MyInt2F        endp
  525.         assume    ds:nothing
  526.  
  527.  
  528.  
  529.  
  530.  
  531. ; TstRmvable-    Checks to see if we can remove this TSR from memory.
  532. ;        Returns the zero flag set if we can remove it, clear
  533. ;        otherwise.
  534.  
  535. TstRmvable    proc    near
  536.         cli
  537.         push    ds
  538.         mov    ax, 0
  539.         mov    ds, ax
  540.  
  541.         cmp    word ptr ds:[9*4], offset MyInt9
  542.         jne    TRDone
  543.         cmp    word ptr ds:[9*4 + 2], seg MyInt9
  544.         jne    TRDone
  545.  
  546.         cmp    word ptr ds:[13h*4], offset MyInt13
  547.         jne    TRDone
  548.         cmp    word ptr ds:[13h*4 + 2], seg MyInt13
  549.         jne    TRDone
  550.  
  551.         cmp    word ptr ds:[16h*4], offset MyInt16
  552.         jne    TRDone
  553.         cmp    word ptr ds:[16h*4 + 2], seg MyInt16
  554.         jne    TRDone
  555.  
  556.         cmp    word ptr ds:[1Ch*4], offset MyInt1C
  557.         jne    TRDone
  558.         cmp    word ptr ds:[1Ch*4 + 2], seg MyInt1C
  559.         jne    TRDone
  560.  
  561.         cmp    word ptr ds:[28h*4], offset MyInt28
  562.         jne    TRDone
  563.         cmp    word ptr ds:[28h*4 + 2], seg MyInt28
  564.         jne    TRDone
  565.  
  566.         cmp    word ptr ds:[2Fh*4], offset MyInt2F
  567.         jne    TRDone
  568.         cmp    word ptr ds:[2Fh*4 + 2], seg MyInt2F
  569. TRDone:         pop    ds
  570.         sti
  571.         ret
  572. TstRmvable    endp
  573. ResidentSeg    ends
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580. cseg        segment    para public 'code'
  581.         assume    cs:cseg, ds:ResidentSeg
  582.  
  583. ; SeeIfPresent-    Checks to see if our TSR is already present in memory.
  584. ;        Sets the zero flag if it is, clears the zero flag if
  585. ;        it is not.
  586.  
  587. SeeIfPresent    proc    near
  588.         push    es
  589.         push    ds
  590.         push    di
  591.         mov    cx, 0ffh        ;Start with ID 0FFh.
  592. IDLoop:        mov    ah, cl
  593.         push    cx
  594.         mov    al, 0            ;Verify presence call.
  595.         int    2Fh
  596.         pop    cx
  597.         cmp    al, 0            ;Present in memory?
  598.         je    TryNext
  599.         strcmpl
  600.         byte    "Keypress Logger TSR",0
  601.         je    Success
  602.  
  603. TryNext:    dec    cl            ;Test USER IDs of 80h..FFh
  604.         js    IDLoop
  605.         cmp    cx, 0            ;Clear zero flag.
  606. Success:    pop    di
  607.         pop    ds
  608.         pop    es
  609.         ret
  610. SeeIfPresent    endp
  611.  
  612.  
  613.  
  614. ; FindID-    Determines the first (well, last actually) TSR ID available
  615. ;        in the multiplex interrupt chain.  Returns this value in
  616. ;        the CL register.
  617. ;
  618. ;        Returns the zero flag set if it locates an empty slot.
  619. ;        Returns the zero flag clear if failure.
  620.  
  621. FindID        proc    near
  622.         push    es
  623.         push    ds
  624.         push    di
  625.  
  626.         mov    cx, 0ffh        ;Start with ID 0FFh.
  627. IDLoop:        mov    ah, cl
  628.         push    cx
  629.         mov    al, 0            ;Verify presence call.
  630.         int    2Fh
  631.         pop    cx
  632.         cmp    al, 0            ;Present in memory?
  633.         je    Success
  634.         dec    cl            ;Test USER IDs of 80h..FFh
  635.         js    IDLoop
  636.         xor    cx, cx
  637.         cmp    cx, 1            ;Clear zero flag
  638. Success:    pop    di
  639.         pop    ds
  640.         pop    es
  641.         ret
  642. FindID        endp
  643.  
  644.  
  645.  
  646. Main        proc
  647.         meminit
  648.  
  649.         mov    ax, ResidentSeg
  650.         mov    ds, ax
  651.  
  652.         mov    ah, 62h            ;Get this program's PSP
  653.         int    21h            ; value.
  654.         mov    PSP, bx
  655.  
  656. ; Before we do anything else, we need to check the command line
  657. ; parameters.  We must have either a valid filename or the
  658. ; command "remove".  If remove appears on the command line, then remove
  659. ; the resident copy from memory using the multiplex (2Fh) interrupt.
  660. ; If remove is not on the command line, we'd better have a filename and
  661. ; there had better not be a copy already loaded into memory.
  662.  
  663.         argc
  664.         cmp    cx, 1            ;Must have exactly 1 parm.
  665.         je    GoodParmCnt
  666.         print
  667.         byte    "Usage:",cr,lf
  668.         byte    "       KeyEval filename",cr,lf
  669.         byte    "or     KeyEval REMOVE",cr,lf,0
  670.         ExitPgm
  671.  
  672.  
  673. ; Check for the REMOVE command.
  674.  
  675. GoodParmCnt:    mov    ax, 1
  676.         argv
  677.         stricmpl
  678.         byte    "REMOVE",0
  679.         jne    TstPresent
  680.  
  681.         call    SeeIfPresent
  682.         je    RemoveIt
  683.         print
  684.         byte    "TSR is not present in memory, cannot remove"
  685.         byte    cr,lf,0
  686.         ExitPgm
  687.  
  688. RemoveIt:    mov    MyTSRID, cl
  689.         printf
  690.         byte    "Removing TSR (ID #%d) from memory...",0
  691.         dword    MyTSRID
  692.  
  693.         mov    ah, cl
  694.         mov    al, 1            ;Remove cmd, ah contains ID
  695.         int    2Fh
  696.         cmp    al, 1            ;Succeed?
  697.         je    RmvFailure
  698.         print
  699.         byte    "removed.",cr,lf,0
  700.         ExitPgm
  701.  
  702. RmvFailure:    print
  703.         byte    cr,lf
  704.         byte    "Could not remove TSR from memory.",cr,lf
  705.         byte    "Try removing other TSRs in the reverse order "
  706.         byte    "you installed them.",cr,lf,0
  707.         ExitPgm
  708.  
  709.  
  710.  
  711. ; Okay, see if the TSR is already in memory.  If so, abort the
  712. ; installation process.
  713.  
  714. TstPresent:     call    SeeIfPresent
  715.         jne    GetTSRID
  716.         print
  717.         byte    "TSR is already present in memory.",cr,lf
  718.         byte    "Aborting installation process",cr,lf,0
  719.         ExitPgm
  720.  
  721.  
  722. ; Get an ID for our TSR and save it away.
  723.  
  724. GetTSRID:    call    FindID
  725.         je    GetFileName
  726.         print
  727.         byte    "Too many resident TSRs, cannot install",cr,lf,0
  728.         ExitPgm
  729.  
  730.  
  731. ; Things look cool so far, check the filename and open the file.
  732.  
  733. GetFileName:    mov    MyTSRID, cl
  734.         printf
  735.         byte    "Keypress logger TSR program",cr,lf
  736.         byte    "TSR ID = %d",cr,lf
  737.         byte    "Processing file:",0
  738.         dword    MyTSRID
  739.  
  740.         puts
  741.         putcr
  742.  
  743.         mov    ah, 3Ch            ;Create file command.
  744.         mov    cx, 0            ;Normal file.
  745.         push    ds
  746.         push    es                ;Point ds:dx at name
  747.         pop    ds
  748.         mov    dx, di
  749.         int    21h            ;Open the file
  750.         jnc    GoodOpen
  751.         print
  752.         byte    "DOS error #",0
  753.         puti
  754.         print
  755.         byte    " opening file.",cr,lf,0
  756.         ExitPgm
  757.  
  758. GoodOpen:    pop    ds
  759.         mov    FileHandle, ax        ;Save file handle.
  760.  
  761.  
  762. InstallInts:    print
  763.         byte    "Installing interrupts...",0
  764.  
  765.  
  766. ; Patch into the INT 9, 13h, 16h, 1Ch, 28h, and 2Fh interrupt vectors.
  767. ; Note that the statements above have made ResidentSeg the current data
  768. ; segment, so we can store the old values directly into
  769. ; the OldIntxx variables.
  770.  
  771.         cli                ;Turn off interrupts!
  772.         mov    ax, 0
  773.         mov    es, ax
  774.         mov    ax, es:[9*4]
  775.         mov    word ptr OldInt9, ax
  776.         mov     ax, es:[9*4 + 2]
  777.         mov    word ptr OldInt9+2, ax
  778.         mov    es:[9*4], offset MyInt9
  779.         mov    es:[9*4+2], seg ResidentSeg
  780.  
  781.         mov    ax, es:[13h*4]
  782.         mov    word ptr OldInt13, ax
  783.         mov     ax, es:[13h*4 + 2]
  784.         mov    word ptr OldInt13+2, ax
  785.         mov    es:[13h*4], offset MyInt13
  786.         mov    es:[13h*4+2], seg ResidentSeg
  787.  
  788.         mov    ax, es:[16h*4]
  789.         mov    word ptr OldInt16, ax
  790.         mov     ax, es:[16h*4 + 2]
  791.         mov    word ptr OldInt16+2, ax
  792.         mov    es:[16h*4], offset MyInt16
  793.         mov    es:[16h*4+2], seg ResidentSeg
  794.  
  795.         mov    ax, es:[1Ch*4]
  796.         mov    word ptr OldInt1C, ax
  797.         mov     ax, es:[1Ch*4 + 2]
  798.         mov    word ptr OldInt1C+2, ax
  799.         mov    es:[1Ch*4], offset MyInt1C
  800.         mov    es:[1Ch*4+2], seg ResidentSeg
  801.  
  802.         mov    ax, es:[28h*4]
  803.         mov    word ptr OldInt28, ax
  804.         mov     ax, es:[28h*4 + 2]
  805.         mov    word ptr OldInt28+2, ax
  806.         mov    es:[28h*4], offset MyInt28
  807.         mov    es:[28h*4+2], seg ResidentSeg
  808.  
  809.         mov    ax, es:[2Fh*4]
  810.         mov    word ptr OldInt2F, ax
  811.         mov     ax, es:[2Fh*4 + 2]
  812.         mov    word ptr OldInt2F+2, ax
  813.         mov    es:[2Fh*4], offset MyInt2F
  814.         mov    es:[2Fh*4+2], seg ResidentSeg
  815.         sti                ;Okay, ints back on.
  816.  
  817. ; We're hooked up, the only thing that remains is to terminate and
  818. ; stay resident.
  819.  
  820.         print
  821.         byte    "Installed.",cr,lf,0
  822.  
  823.  
  824.         mov    dx, EndResident        ;Compute size of program.
  825.         sub    dx, PSP
  826.         mov    ax, 3100h        ;DOS TSR command.
  827.         int    21h
  828. Main        endp
  829. cseg        ends
  830.  
  831. sseg        segment    para stack 'stack'
  832. stk        db    1024 dup ("stack   ")
  833. sseg        ends
  834.  
  835. zzzzzzseg    segment    para public 'zzzzzz'
  836. LastBytes    db    16 dup (?)
  837. zzzzzzseg    ends
  838.         end    Main
  839.